package org.jugile.jsontmpl.domain;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jugile.util.Jugile;

public class UoW {

	private Map<String,Object> threadlocalStore = new HashMap<String,Object>();
	public Object get(String key) { return threadlocalStore.get(key); }
	public void put(String key, Object value) { threadlocalStore.put(key,value); }

	private Set<Entity> modified = new HashSet<Entity>();
	private Set<Entity> deleted = new HashSet<Entity>();
	private Set<Mapping> mappings = new HashSet<Mapping>();
	private Set<Mapping> delmappings = new HashSet<Mapping>();

	public void setModified(Entity o) { modified.add(o); }
	public void setDeleted(Entity o) { deleted.add(o); }
	
	public void addMapping(String tname, Entity o1, Entity o2, String name) {
		mappings.add(new Mapping(tname,o1,o2,name));
	}
	public void removeMapping(String tname, Entity o1, Entity o2, String name) {
		delmappings.add(new Mapping(tname,o1,o2,name));
	}
	
	public void removeModified(Entity o) { 	modified.remove(o);	}

	public Set<Entity> getModified() { return modified; }
	public Set<Entity> getDeleted() { return deleted; }
	public Set<Mapping> getMappings() { return mappings; }
	public Set<Mapping> getDelMappings() { return delmappings; }

	public List<Entity> getModifiedSorted() { return sort(modified); }
	public List<Entity> getDeletedSorted() { return sort(deleted); }

	public List<Entity> sort(Set<Entity> set) {
		List<Entity> res = new ArrayList<Entity>(set);
		Collections.sort(res, new EntityComparator());
		return res;
	}

	public class EntityComparator implements Comparator {
		public int compare(Object o1, Object o2) {
			if (o1 == null && o2 == null) return 0;
			if (o1 == null) return 1;
			if (o2 == null) return -1;
			String c1 = o1.getClass().getName();
			String c2 = o2.getClass().getName();
			int c = c1.compareTo(c2);
			if (c != 0) return c;
			Long id1 = ((Entity)o1).id();
			Long id2 = ((Entity)o1).id();
			return id1.compareTo(id2);
		}
	}
	
	
	public int getCommitSize() {
		return modified.size() + deleted.size() + mappings.size() + delmappings.size();
	}
	
	public String getInfo() {
		String info = "UoW:";
		info += " modified: "+modified.size();
		info += " deleted: "+deleted.size();
		info += " n-n: "+mappings.size();
		info += " n-n deleted: "+delmappings.size();
		return info;
	}
	
	public class Mapping {
		String tname;
		String name; // name of the connection
		Entity o1;
		Entity o2;
		public Mapping(String tname, Entity o1, Entity o2, String name) {
			if (o1 == null || o2 == null) throw new RuntimeException("null mappinng");
			this.o1 = o1;
			this.o2 = o2;
			this.tname = tname;
			this.name = name;
		}
		public Entity getO1() { return o1; }
		public Entity getO2() { return o2; }
		public String getTableName() { return tname; }
		public String getName() { return name; }
		
		public boolean equals(Object m2) {
			if (m2 == null) return false;
			if (!(m2 instanceof Mapping)) return false;
			Mapping mm2 = (Mapping)m2;
			if (!mm2.o1.equals(o1)) return false;
			if (!mm2.o2.equals(o2)) return false;
			if (!Jugile.eq(tname,mm2.tname)) return false;
			return true;
		}
		
		public int hashCode() {
			return o1.hashCode() + o2.hashCode() + tname.hashCode();
		}
	}

}